#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
天翼云盘上传操作模块
包含文件上传和秒传功能的实现
"""

import os
import json
import time
import threading
import requests
from typing import Dict, List, Optional, Any, Union, BinaryIO
from urllib.parse import urlparse, parse_qs
from .utils import FormatUtils, TimeUtils, ClientUtils


class UploadOperations:
    """天翼云盘上传操作类"""
    
    def __init__(self, client):
        """初始化上传操作
        
        Args:
            client: 天翼云盘客户端实例
        """
        self.client = client
        self.logger = client.logger
        self.session = client.session
    
    def check_fast_upload(self, filename: str, file_size: int, file_md5: str, 
                         parent_id: str = None) -> bool:
        """检查文件是否支持秒传
        
        Args:
            filename: 文件名
            file_size: 文件大小（字节）
            file_md5: 文件MD5值
            parent_id: 父文件夹ID
        
        Returns:
            bool: 是否支持秒传
        """
        if not self.client.check_login_status():
            if not self.client.login():
                return False
        
        if parent_id is None:
            parent_id = "-11"  # 根目录
        
        url = "https://api.cloud.189.cn/createUploadFile.action"
        params = {
            'parentFolderId': parent_id,
            'fileName': filename,
            'fileSize': file_size,
            'fileMd5': file_md5,
            'sliceSize': self._calculate_slice_size(file_size),
            'lazyCheck': 1
        }
        params.update(self.client.client_suffix())
        
        headers = self.client.signature_header(url, "POST")
        
        try:
            response = self.session.post(url, data=params, headers=headers)
            if response.status_code == 200:
                result = response.json()
                if result.get('code') == 'SUCCESS':
                    # 如果返回文件信息，说明支持秒传
                    return 'file' in result
                else:
                    self.logger.debug(f"秒传检查失败: {result.get('msg')}")
        except Exception as e:
            self.logger.error(f"秒传检查请求失败: {e}")
        
        return False
    
    def fast_upload(self, filename: str, file_size: int, file_md5: str, 
                   parent_id: str = None) -> Optional[Dict[str, Any]]:
        """执行秒传
        
        Args:
            filename: 文件名
            file_size: 文件大小（字节）
            file_md5: 文件MD5值
            parent_id: 父文件夹ID
        
        Returns:
            Dict[str, Any]: 秒传成功返回文件信息，失败返回None
        """
        if not self.client.check_login_status():
            if not self.client.login():
                return None
        
        if parent_id is None:
            parent_id = "-11"  # 根目录
        
        url = "https://api.cloud.189.cn/createUploadFile.action"
        params = {
            'parentFolderId': parent_id,
            'fileName': filename,
            'fileSize': file_size,
            'fileMd5': file_md5,
            'sliceSize': self._calculate_slice_size(file_size),
            'lazyCheck': 1
        }
        params.update(self.client.client_suffix())
        
        headers = self.client.signature_header(url, "POST")
        
        try:
            response = self.session.post(url, data=params, headers=headers)
            if response.status_code == 200:
                result = response.json()
                if result.get('code') == 'SUCCESS' and 'file' in result:
                    file_info = result['file']
                    self.logger.info(f"文件秒传成功: {filename}")
                    return {
                        'id': file_info.get('id'),
                        'name': file_info.get('name'),
                        'size': file_info.get('size'),
                        'type': 'file',
                        'md5': file_info.get('md5'),
                        'create_time': file_info.get('createDate'),
                        'modify_time': file_info.get('lastOpTime')
                    }
                else:
                    self.logger.debug(f"秒传失败: {result.get('msg')}")
        except Exception as e:
            self.logger.error(f"秒传请求失败: {e}")
        
        return None
    
    def upload_file(self, file_data: Union[str, bytes, BinaryIO], filename: str, 
                   parent_id: str = None, overwrite: bool = True, 
                   try_fast_upload: bool = True) -> Optional[Dict[str, Any]]:
        """上传文件
        
        Args:
            file_data: 文件数据（文件路径、字节数据或文件对象）
            filename: 文件名
            parent_id: 父文件夹ID，None表示根目录
            overwrite: 是否覆盖同名文件
            try_fast_upload: 是否尝试秒传
        
        Returns:
            Dict[str, Any]: 上传成功返回文件信息，失败返回None
        """
        if not self.client.check_login_status():
            if not self.client.login():
                return None
        
        # 计算文件大小和MD5
        file_size = self.client.get_file_size(file_data)
        file_md5 = self.client.calculate_file_md5(file_data)
        
        self.logger.info(f"开始上传文件: {filename} (大小: {file_size} 字节, MD5: {file_md5})")
        
        # 尝试秒传
        if try_fast_upload:
            self.logger.info("尝试秒传...")
            fast_result = self.fast_upload(filename, file_size, file_md5, parent_id)
            if fast_result:
                return fast_result
            self.logger.info("秒传失败，开始普通上传")
        
        # 普通上传
        return self._upload_file_stream(file_data, filename, file_size, file_md5, parent_id, overwrite)
    
    def _upload_file_stream(self, file_data: Union[str, bytes, BinaryIO], filename: str, 
                           file_size: int, file_md5: str, parent_id: str = None, 
                           overwrite: bool = True) -> Optional[Dict[str, Any]]:
        """普通上传文件流
        
        Args:
            file_data: 文件数据
            filename: 文件名
            file_size: 文件大小
            file_md5: 文件MD5
            parent_id: 父文件夹ID
            overwrite: 是否覆盖
        
        Returns:
            Dict[str, Any]: 上传结果
        """
        if parent_id is None:
            parent_id = "-11"  # 根目录
        
        self.logger.info(f"开始上传文件流: {filename}, 大小: {file_size}, MD5: {file_md5}")
        
        # 初始化上传
        upload_info = self._init_upload(filename, file_size, file_md5, parent_id)
        if not upload_info:
            self.logger.error("初始化上传失败")
            return None
        
        self.logger.info(f"初始化上传成功: {upload_info}")
        
        upload_file_id = upload_info.get('uploadFileId')
        file_upload_url = upload_info.get('fileUploadUrl')
        
        if not upload_file_id or not file_upload_url:
            self.logger.error("获取上传信息失败")
            return None
        
        # 直接上传文件到指定URL
        self.logger.info(f"开始上传文件到: {file_upload_url}")
        
        # 准备文件数据
        if isinstance(file_data, str):
            # 文件路径
            with open(file_data, 'rb') as f:
                file_content = f.read()
        elif isinstance(file_data, bytes):
            file_content = file_data
        else:
            # 文件对象
            file_content = file_data.read()
        
        # 根据Go源码设置请求头
        headers = {
            'ResumePolicy': '1',
            'Expect': '100-continue'
        }
        
        # 根据是否为家庭云设置不同的头部（暂时设为个人云模式）
        headers['Edrive-UploadFileId'] = str(upload_file_id)
        
        # 生成签名头并合并
        signature_headers = self.client.signature_header(file_upload_url, 'PUT', '')
        headers.update(signature_headers)
        
        print(f"上传URL: {file_upload_url}")
        print(f"请求头: {headers}")
        
        try:
            # 使用PUT方法上传文件（参考Go源码）
            response = self.session.put(file_upload_url, data=file_content, headers=headers)
            self.logger.info(f"文件上传响应: status_code={response.status_code}")
            
            if response.status_code == 200:
                self.logger.info("文件上传成功，开始提交上传")
                # 提交上传，使用从初始化上传返回的fileCommitUrl
                result = self._commit_upload(upload_info['fileCommitUrl'], upload_file_id)
                if result:
                    self.logger.info(f"提交上传成功: {result}")
                    # 返回标准格式的结果
                    return {
                        'success': True,
                        'file_id': result.get('id'),
                        'name': result.get('name'),
                        'size': int(result.get('size', 0)) if result.get('size') else 0,
                        'md5': result.get('md5'),
                        'create_time': result.get('createDate'),
                        'type': 'file'
                    }
                else:
                    self.logger.error("提交上传失败")
            else:
                self.logger.error(f"文件上传失败: {response.status_code}, 响应: {response.text}")
        except Exception as e:
            self.logger.error(f"文件上传请求失败: {e}")
        
        return None
    
    def _init_upload(self, filename: str, file_size: int, file_md5: str, 
                    parent_id: str) -> Optional[Dict[str, Any]]:
        """初始化上传
        
        Args:
            filename: 文件名
            file_size: 文件大小
            file_md5: 文件MD5
            parent_id: 父文件夹ID
        
        Returns:
            Dict[str, Any]: 初始化结果
        """
        url = "https://api.cloud.189.cn/createUploadFile.action"
        
        # 根据Go源码使用表单数据而非查询参数
        form_data = {
            'parentFolderId': parent_id,
            'fileName': filename,
            'size': str(file_size),
            'md5': file_md5,
            'opertype': '3',
            'flag': '1',
            'resumePolicy': '1',
            'isLog': '0'
        }
        
        headers = self.client.signature_header(url, "POST")
        
        self.logger.info(f"初始化上传请求: URL={url}, form_data={form_data}")
        
        try:
            response = self.session.post(url, data=form_data, headers=headers)
            self.logger.info(f"初始化上传响应: status_code={response.status_code}")
            
            if response.status_code == 200:
                # 解析JSON响应
                try:
                    result = response.json()
                    if result.get('res_code') == 0:
                        upload_info = {
                            'uploadFileId': result.get('uploadFileId'),
                            'fileUploadUrl': result.get('fileUploadUrl'),
                            'fileCommitUrl': result.get('fileCommitUrl'),
                            'fileDataExists': result.get('fileDataExists', 0)
                        }
                        self.logger.info(f"初始化上传成功: {upload_info}")
                        return upload_info
                    else:
                        self.logger.error(f"初始化上传失败: {result.get('res_message')}")
                except Exception as e:
                    self.logger.error(f"解析JSON响应失败: {e}, 响应: {response.text}")
            else:
                self.logger.error(f"初始化上传HTTP错误: {response.status_code}, 响应: {response.text}")
        except Exception as e:
            self.logger.error(f"初始化上传请求失败: {e}")
        
        return None
    
    def _get_upload_urls(self, upload_file_id: str, file_size: int) -> Optional[List[str]]:
        """获取上传链接
        
        Args:
            upload_file_id: 上传文件ID
            file_size: 文件大小
        
        Returns:
            List[str]: 上传链接列表
        """
        slice_size = self._calculate_slice_size(file_size)
        slice_count = (file_size + slice_size - 1) // slice_size
        
        url = "https://api.cloud.189.cn/open/file/getUploadUrls.action"
        params = {
            'uploadFileId': upload_file_id,
            'partInfo': json.dumps([{'partNumber': i + 1} for i in range(slice_count)])
        }
        params.update(self.client.client_suffix())
        
        # 将参数转换为字符串用于签名
        params_str = "&".join([f"{k}={v}" for k, v in params.items()])
        headers = self.client.signature_header(url, "POST", params_str)
        
        try:
            response = self.session.post(url, data=params, headers=headers)
            if response.status_code == 200:
                result = response.json()
                if result.get('code') == 'SUCCESS':
                    upload_urls = result.get('uploadUrls', {})
                    return [upload_urls.get(str(i + 1)) for i in range(slice_count)]
                else:
                    self.logger.error(f"获取上传链接失败: {result.get('msg')}")
        except Exception as e:
            self.logger.error(f"获取上传链接请求失败: {e}")
        
        return None
    
    def _upload_part(self, upload_url: str, data: bytes, part_number: int) -> bool:
        """上传分片
        
        Args:
            upload_url: 上传链接
            data: 分片数据
            part_number: 分片编号
        
        Returns:
            bool: 上传是否成功
        """
        try:
            response = self.session.put(upload_url, data=data)
            if response.status_code == 200:
                return True
            else:
                self.logger.error(f"上传分片失败，状态码: {response.status_code}")
        except Exception as e:
            self.logger.error(f"上传分片请求失败: {e}")
        
        return False
    
    def _commit_upload(self, commit_url: str, upload_file_id: str) -> Optional[Dict[str, Any]]:
        """提交上传
        
        Args:
            commit_url: 提交上传的URL
            upload_file_id: 上传文件ID
        
        Returns:
            Dict[str, Any]: 提交结果
        """
        # 根据Go源码，使用表单数据而非查询参数
        form_data = {
            'opertype': '1',
            'resumePolicy': '1',
            'uploadFileId': upload_file_id,
            'isLog': '0'
        }
        
        headers = self.client.signature_header(commit_url, "POST")
        
        self.logger.info(f"提交上传请求: URL={commit_url}, form_data={form_data}")
        
        try:
            response = self.session.post(commit_url, data=form_data, headers=headers)
            self.logger.info(f"提交上传响应: status_code={response.status_code}")
            
            if response.status_code == 200:
                try:
                    # 检查响应是否为XML格式
                    response_text = response.text.strip()
                    if response_text.startswith('<?xml'):
                        # 解析XML响应
                        import xml.etree.ElementTree as ET
                        root = ET.fromstring(response_text)
                        
                        # 提取文件信息
                        file_info = {
                            'id': root.find('id').text if root.find('id') is not None else None,
                            'name': root.find('name').text if root.find('name') is not None else None,
                            'size': root.find('size').text if root.find('size') is not None else None,
                            'md5': root.find('md5').text if root.find('md5') is not None else None,
                            'createDate': root.find('createDate').text if root.find('createDate') is not None else None,
                            'rev': root.find('rev').text if root.find('rev') is not None else None
                        }
                        self.logger.info(f"提交上传成功: {file_info}")
                        return file_info
                    else:
                        # 尝试解析JSON响应
                        result = response.json()
                        if result.get('res_code') == 0:
                            file_info = result.get('file', {})
                            self.logger.info(f"提交上传成功: {file_info}")
                            return file_info
                        else:
                            self.logger.error(f"提交上传失败: {result.get('res_message')}")
                except Exception as e:
                    self.logger.error(f"解析响应失败: {e}, 响应: {response.text}")
            else:
                self.logger.error(f"提交上传HTTP错误: {response.status_code}, 响应: {response.text}")
        except Exception as e:
            self.logger.error(f"提交上传请求失败: {e}")
        
        return None
    
    def _calculate_slice_size(self, file_size: int) -> int:
        """计算分片大小
        
        Args:
            file_size: 文件大小（字节）
        
        Returns:
            int: 分片大小（字节）
        """
        if file_size <= 1024 * 1024 * 10:  # 10MB以下
            return 1024 * 1024  # 1MB
        elif file_size <= 1024 * 1024 * 100:  # 100MB以下
            return 1024 * 1024 * 5  # 5MB
        else:
            return 1024 * 1024 * 10  # 10MB
    
    def _read_slice_data(self, file_data: Union[str, bytes, BinaryIO], 
                        start_pos: int, size: int) -> Optional[bytes]:
        """读取分片数据
        
        Args:
            file_data: 文件数据
            start_pos: 起始位置
            size: 读取大小
        
        Returns:
            bytes: 分片数据
        """
        try:
            if isinstance(file_data, str):
                # 文件路径
                with open(file_data, "rb") as f:
                    f.seek(start_pos)
                    return f.read(size)
            elif isinstance(file_data, bytes):
                # 字节数据
                return file_data[start_pos:start_pos + size]
            elif hasattr(file_data, 'read') and hasattr(file_data, 'seek'):
                # 文件对象
                file_data.seek(start_pos)
                return file_data.read(size)
            else:
                raise ValueError("不支持的文件数据类型")
        except Exception as e:
            self.logger.error(f"读取分片数据失败: {e}")
            return None